package com.yu.mall.service;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.yu.mall.entity.Bill;
import com.yu.mall.entity.Order;
import com.yu.mall.entity.OrderConfirm;
import com.yu.mall.entity.Pay;
import com.yu.mall.entity.PayExample;
import com.yu.mall.entity.User;
import com.yu.mall.enums.PayChannelEnum;
import com.yu.mall.enums.PayStatusEnum;
import com.yu.mall.exception.ServiceException;
import com.yu.mall.mapper.BillMapper;
import com.yu.mall.mapper.OrderConfirmMapper;
import com.yu.mall.mapper.OrderMapper;
import com.yu.mall.mapper.PayMapper;
import com.yu.mall.mapper.UserMapper;
import com.yu.mall.util.JDUtils;
import com.yu.mall.util.WechatPayUtil;
import com.yu.mall.vo.ResultMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * @Desc: 支付服务
 * @Author: zhangyicheng
 * @Date: 2021/3/4
 */
@Service
@Transactional
@Slf4j
public class PayService {

    @Resource
    PayMapper payMapper;
    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OrderConfirmMapper confirmMapper;

    @Autowired
    private BillMapper billMapper;

    private static List<String> succ_state_list = Arrays.asList("SUCCESS");
    private static List<String> fail_state_list = Arrays.asList("REFUND","CLOSED","REVOKED","PAYERROR");
    private static List<String> refund_succ_state_list = Arrays.asList("SUCCESS");
    private static List<String> refund_fail_state_list = Arrays.asList("CLOSED","ABNORMAL");

    public ResultMessage createPay(String orderId, String outTradeNo, String openid,String payType,String description) {
        try {
            Order order = new Order();
            order.setOrderNo(outTradeNo);
            order = orderMapper.selectOne(order);
            String jdOrderNo = order.getJdOrderNo();
            if("1".equals(payType)){
                User user = userMapper.selectByPrimaryKey(openid);
                if(user.getBalance().compareTo(order.getOrderPriceTotal()) == -1){
                    return ResultMessage.fail("余额不足");
            }
            }

            BigDecimal priceTotal = order.getOrderPriceTotal();
            int price = priceTotal.multiply(new BigDecimal(100)).intValue();
            if(!"1".equals(payType)){
                ResultMessage<JSONObject> payResult = WechatPayUtil.createOrder(outTradeNo, price, openid, description);
                if(!payResult.getResult()){
                    return ResultMessage.fail("请求微信支付失败");
                }
                Pay pay = getDefPay(orderId, outTradeNo, openid, price);
                pay.setPayChannel(PayChannelEnum.WECHAT.getCode());
                this.insert(pay);
                JSONObject data = new JSONObject();
                data.putAll(payResult.getData());
                data.put("pay",pay);
                return ResultMessage.success(data);
            }else{
                boolean flag = transactionTemplate.execute(new TransactionCallback<Boolean>() {
                    @Override
                    public Boolean doInTransaction(TransactionStatus transactionStatus) {
                        try {
                            User user = userMapper.selectByPrimaryKey(openid);
                            User user1 = new User();
                            user1.setOpenid(user.getOpenid());
                            user1.setBalance(user.getBalance().subtract(priceTotal));
                            user1.setUpdateTime(new Date());
                            int i = userMapper.updateByPrimaryKeySelective(user1);
                            if(i <= 0){
                               throw new ServiceException("支付失败");
                            }
                            Example example = new Example(Order.class);
                            example.createCriteria().andEqualTo("orderNo",orderId);
                            Order order = orderMapper.selectOneByExample(example);
                            order.setStatus("2");
                            order.setUpdateTime(new Date());
                            order.setPayType(payType);
                            order.setPayTime(new Date());
                            i = orderMapper.updateByExampleSelective(order, example);
                            if( i <= 0){
                                throw new ServiceException("支付失败");
                            }

                            Bill bill = new Bill();
                            bill.setAmount(order.getOrderPriceTotal());
                            bill.setJdAmount(order.getJdOrderPriceTotal());
                            bill.setCreateTime(new Date());
                            bill.setPayType(payType);
                            bill.setOrderNo(orderId);
                            bill.setJdOrderNo(order.getJdOrderNo());
                            bill.setType("1");
                            billMapper.insertSelective(bill);
                            Pay pay = getDefPay(orderId, outTradeNo, openid, price);
                            pay.setPayChannel(PayChannelEnum.POINTS.getCode());
                            pay.setStatus(PayStatusEnum.SUCC.getCode());
                            pay.setConfirmTime(new Date());
                            insert(pay);
                            String s = JDUtils.confirmOrder(jdOrderNo);
                            if(!"true".equals(s)){
                                throw new ServiceException("确认订单失败");
                            };
                            return true;
                        } catch (Exception e) {
                            transactionStatus.setRollbackOnly();
                            return false;
                        }
                    }
                });
                if(!flag){
                    return ResultMessage.fail("支付失败");
                }
                return ResultMessage.success("支付成功");
            }
        } catch (Exception e) {
            log.error("微信支付出错", e);
        }
        return ResultMessage.fail("失败");
    }

    public ResultMessage<String> queryPay(String outTradeNo) {
        try {
            Pay pay = this.queryByOutTradeNo(outTradeNo);
            if(null == pay){
                return ResultMessage.fail("支付记录不存在");
            }
            if(PayStatusEnum.SUCC.getCode().equals(pay.getStatus())
                || PayStatusEnum.FAIL.getCode().equals(pay.getStatus())){
                return ResultMessage.success(pay.getStatus());
            }
            ResultMessage<JSONObject> queryOrderResult = WechatPayUtil.queryOrder(outTradeNo);
            if(!queryOrderResult.getResult()){
                return ResultMessage.fail("查询失败");
            }
            JSONObject data = queryOrderResult.getData();
            String transaction_id = data.getString("transaction_id");
            String trade_state = data.getString("trade_state");
            String success_time = data.getString("success_time");
            Pay updateBean = new Pay();
            updateBean.setId(pay.getId());
            updateBean.setTransactionId(transaction_id);
            if(succ_state_list.contains(trade_state)){
                updateBean.setStatus(PayStatusEnum.SUCC.getCode());
                updateBean.setConfirmTime(DateUtil.parse(success_time,"yyyy-MM-dd'T'HH:mm:ssXXX"));
            }else if(fail_state_list.contains(trade_state)){
                updateBean.setStatus(PayStatusEnum.FAIL.getCode());
            }else{
                updateBean.setStatus(PayStatusEnum.ING.getCode());
            }
            payMapper.updateByPrimaryKeySelective(updateBean);
            return ResultMessage.success(updateBean.getStatus());
        } catch (Exception e) {
            log.error("微信支付订单查询出错", e);
        }
        return ResultMessage.fail("失败");
    }

    public ResultMessage<String> refundPay(String outTradeNo,String outRefundNo,int refundAmount,String reason) {
        try {
            Pay pay = this.queryByOutTradeNo(outTradeNo);
            if(null == pay){
                return ResultMessage.fail("支付记录不存在");
            }
            if(!PayStatusEnum.SUCC.getCode().equals(pay.getStatus())){
                return ResultMessage.fail("非支付成功订单不支持退款");
            }
            ResultMessage<JSONObject> refundResult = WechatPayUtil.refundOrder(outTradeNo, outRefundNo, pay.getAmount(), refundAmount, reason);
            if(!refundResult.getResult()){
                return ResultMessage.fail("请求微信退款失败");
            }
            JSONObject data = refundResult.getData();
            String refund_id = data.getString("refund_id");
            String status = data.getString("status");
            String success_time = data.getString("success_time");
            Pay refundBean = new Pay();
            refundBean.setType("1");
            refundBean.setRefundId(refund_id);
            refundBean.setOutTradeNo(outTradeNo);
            refundBean.setOutRefundNo(outRefundNo);
            refundBean.setRefundAmount(refundAmount);
            refundBean.setPayChannel("WECHAT");
            if(refund_succ_state_list.contains(status)){
                refundBean.setRefundStatus(PayStatusEnum.SUCC.getCode());
                refundBean.setRefundTime(DateUtil.parse(success_time,"yyyy-MM-dd'T'HH:mm:ssXXX"));
            }else if(refund_fail_state_list.contains(status)){
                refundBean.setRefundStatus(PayStatusEnum.FAIL.getCode());
            }else{
                refundBean.setRefundStatus(PayStatusEnum.ING.getCode());
            }
            payMapper.insertSelective(refundBean);
            if(refundBean.getRefundStatus().equals(PayStatusEnum.FAIL.getCode())){
                return ResultMessage.fail("退款失败");
            }
            return ResultMessage.success(refundBean.getRefundStatus());
        } catch (Exception e) {
            log.error("微信订单退款出错", e);
        }
        return ResultMessage.fail("失败");
    }

    public ResultMessage<String> queryRefundPay(String outRefundNo) {
        try {
            Pay pay = this.queryByOutRefundNo(outRefundNo);
            if(null == pay){
                return ResultMessage.fail("退款记录不存在");
            }
            if(PayStatusEnum.SUCC.getCode().equals(pay.getRefundStatus())){
                return ResultMessage.success(pay.getRefundStatus());
            }
            if(PayStatusEnum.FAIL.getCode().equals(pay.getRefundStatus())){
                return ResultMessage.success(pay.getRefundStatus());
            }
            ResultMessage<JSONObject> queryRefundOrderResult = WechatPayUtil.queryRefundOrder(outRefundNo);
            if(!queryRefundOrderResult.getResult()){
                return ResultMessage.fail("查询失败");
            }
            JSONObject data = queryRefundOrderResult.getData();
            String status = data.getString("status");
            String success_time = data.getString("success_time");
            Pay updateBean = new Pay();
            updateBean.setId(pay.getId());
            String refundStatus = PayStatusEnum.ING.getCode();
            if(refund_succ_state_list.contains(status)){
                updateBean.setRefundStatus(PayStatusEnum.SUCC.getCode());
                updateBean.setRefundTime(DateUtil.parse(success_time,"yyyy-MM-dd'T'HH:mm:ssXXX"));
                refundStatus = PayStatusEnum.SUCC.getCode();
                payMapper.updateByPrimaryKeySelective(updateBean);
            }else if(refund_fail_state_list.contains(status)){
                updateBean.setRefundStatus(PayStatusEnum.FAIL.getCode());
                refundStatus = PayStatusEnum.FAIL.getCode();
                payMapper.updateByPrimaryKeySelective(updateBean);
            }
            return ResultMessage.success(refundStatus);
        } catch (Exception e) {
            log.error("微信退款订单查询出错", e);
        }
        return ResultMessage.fail("失败");
    }

    public boolean handlePayNotify(JSONObject data) {
        try {
            String transaction_id = data.getString("transaction_id");
            String trade_state = data.getString("trade_state");
            String success_time = data.getString("success_time");
            String out_trade_no = data.getString("out_trade_no");
            Pay pay = this.queryByOutTradeNo(out_trade_no);
            if(null == pay){
                return true;
            }
            if(!PayStatusEnum.ING.getCode().equals(pay.getStatus())){
                return true;
            }
            Pay updateBean = new Pay();
            updateBean.setId(pay.getId());
            updateBean.setTransactionId(transaction_id);
            if(succ_state_list.contains(trade_state)){
                Example example = new Example(Order.class);
                example.createCriteria().andEqualTo("orderNo",out_trade_no);
                Order order = orderMapper.selectOneByExample(example);
                //调用京东确订单
                boolean flag = true;
                try {
                    String s = JDUtils.confirmOrder(order.getJdOrderNo());
                    flag = "true".equals(s);
                } catch (Exception e) {
                    log.error("确认订单失败：" + order.getOrderNo(),e);
                    OrderConfirm confirm = new OrderConfirm();
                    confirm.setCreateTime(new Date());
                    confirm.setJdOrderNo(order.getJdOrderNo());
                    confirm.setNum(0);
                    if(e instanceof ServiceException){
                        ServiceException serviceException =  (ServiceException) e;
                        confirm.setReason(serviceException.getErrorMsg());
                    }else {
                        confirm.setReason("网络不通");
                    }
                    confirmMapper.insertSelective(confirm);
                    flag = false;
                }
                if(flag){
                    order.setStatus("2");
                }else {
                    log.error("确认订单失败：" + order.getOrderNo());
                    OrderConfirm confirm = new OrderConfirm();
                    confirm.setCreateTime(new Date());
                    confirm.setJdOrderNo(order.getJdOrderNo());
                    confirm.setNum(0);
                    confirm.setReason("京东接口返回错误");
                    confirmMapper.insertSelective(confirm);
                }
                order.setUpdateTime(new Date());
                order.setPayType("2");
                order.setPayTime(new Date());
                int i = orderMapper.updateByExampleSelective(order, example);
                if( i <= 0){
                    return false;
                }
                Bill bill = new Bill();
                bill.setAmount(order.getOrderPriceTotal());
                bill.setJdAmount(order.getJdOrderPriceTotal());
                bill.setCreateTime(new Date());
                bill.setPayType("2");
                bill.setOrderNo(order.getOrderNo());
                bill.setJdOrderNo(order.getJdOrderNo());
                bill.setType("1");
                billMapper.insertSelective(bill);
                updateBean.setOrderId(order.getOrderNo());
                updateBean.setStatus(PayStatusEnum.SUCC.getCode());
                updateBean.setConfirmTime(DateUtil.parse(success_time,"yyyy-MM-dd'T'HH:mm:ssXXX"));
                payMapper.updateByPrimaryKeySelective(updateBean);
                return true;
            }else if(fail_state_list.contains(trade_state)){
                updateBean.setStatus(PayStatusEnum.FAIL.getCode());
            }else{
                updateBean.setStatus(PayStatusEnum.ING.getCode());
            }
            payMapper.updateByPrimaryKeySelective(updateBean);
            if(!PayStatusEnum.SUCC.getCode().equals(updateBean.getStatus())){
                return true;
            }

        } catch (Exception e) {
            log.error("处理微信支付回调失败，{}",data,e);
        }
        return false;
    }

    /*public boolean handleRefundNotify(JSONObject data) {
        try {
            String refund_status = data.getString("refund_status");
            String success_time = data.getString("success_time");
            String out_trade_no = data.getString("out_trade_no");
            Pay pay = this.queryByOutTradeNo(out_trade_no);
            if(null == pay){
                return true;
            }
            if(pay.getIsRefund() && PayStatusEnum.SUCC.getCode().equals(pay.getRefundStatus())){
                return true;
            }
            if(PayStatusEnum.FAIL.getCode().equals(pay.getRefundStatus())){
                return true;
            }
            Pay updateBean = new Pay();
            updateBean.setId(pay.getId());
            if(refund_succ_state_list.contains(refund_status)){
                updateBean.setIsRefund(true);
                updateBean.setRefundStatus(PayStatusEnum.SUCC.getCode());
                updateBean.setRefundTime(DateUtil.parse(success_time,"yyyy-MM-dd'T'HH:mm:ssXXX"));
                payMapper.updateByPrimaryKey(updateBean);
            }else if(refund_fail_state_list.contains(refund_status)){
                updateBean.setIsRefund(false);
                updateBean.setRefundStatus(PayStatusEnum.FAIL.getCode());
                payMapper.updateByPrimaryKey(updateBean);
            }
            return true;
        } catch (Exception e) {
            log.error("处理微信退款回调失败，{}",data,e);
        }
        return false;
    }*/

    public int insert(Pay pay) {
        return payMapper.insertSelective(pay);
    }

    private Pay getDefPay(String orderId, String outTradeNo, String openid,int amount){
        Pay pay = new Pay();
        pay.setOrderId(orderId);
        pay.setOpenid(openid);
        pay.setOutTradeNo(outTradeNo);
        pay.setAmount(amount);
        pay.setStatus(PayStatusEnum.ING.getCode());
        pay.setType("0");
        return pay;
    }

    public Pay queryByOutTradeNo(String outTradeNo){
        PayExample example = new PayExample();
        example.createCriteria().andOutTradeNoEqualTo(outTradeNo).andTypeEqualTo("0");
        List<Pay> pays = payMapper.selectByExample(example);
        return CollectionUtils.isEmpty(pays)?null:pays.get(0);
    }

    public Pay queryByOutRefundNo(String outRefundNo){
        PayExample example = new PayExample();
        example.createCriteria().andOutRefundNoEqualTo(outRefundNo).andTypeEqualTo("1");
        List<Pay> pays = payMapper.selectByExample(example);
        return CollectionUtils.isEmpty(pays)?null:pays.get(0);
    }
}
